<!doctype html>
<html lang="zh-cn">
<head>

    <meta charset="utf-8">
    <meta name="generator" content="Hugo 0.57.2" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>ES6概览&#43;入门 | The Sky of OtsWang</title>
    <meta property="og:title" content="ES6概览&#43;入门 - The Sky of OtsWang">
    <meta property="og:type" content="article">
        
    <meta property="article:published_time" content="2018-11-19T08:46:50&#43;08:00">
        
        
    <meta property="article:modified_time" content="2018-11-23T16:05:25&#43;08:00">
        
    <meta name="Keywords" content="golang,go语言,otswang,java,博客,python">
    <meta name="description" content="阮一峰ES6标准入门学习">
        
    <meta name="author" content="OtsWang">
    <meta property="og:url" content="https://otswang.gitee.io/hugo/post/webapp/es6/">
    <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">

    <link rel="stylesheet" href="/hugo/css/normalize.css">
    
        <link rel="stylesheet" href="/hugo/css/prism.css">
    
    <link rel="stylesheet" href="/hugo/css/style.css">
    <script type="text/javascript" src="//cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>

    


    
    
</head>

<body>
<header id="header" class="clearfix">
    <div class="container">
        <div class="col-group">
            <div class="site-name ">
                
                    <a id="logo" href="https://otswang.gitee.io/hugo/">
                        The Sky of OtsWang
                    </a>
                
                <p class="description">擅长写HelloWorld的小小码农</p>
            </div>
            <div>
                <nav id="nav-menu" class="clearfix">
                    
                    
                    <a  href="https://otswang.gitee.io/hugo/" title="Home">Home</a>
                    
                    <a  href="https://otswang.gitee.io/hugo/tags/" title="Tags">Tags</a>
                    
                    <a  href="https://otswang.gitee.io/hugo/categories/" title="Categories">Categories</a>
                    
                    <a  href="https://otswang.gitee.io/hugo/archives/" title="Archives">Archives</a>
                    
                    <a  href="https://otswang.gitee.io/hugo/about/" title="About">About</a>
                    
                </nav>
            </div>
        </div>
    </div>
</header>


<div id="body">
    <div class="container">
        <div class="col-group">

            <div class="col-8" id="main">
                <div class="res-cons">
                    <article class="post">
                        <header>
                            <h1 class="post-title">ES6概览&#43;入门</h1>
                        </header>
                        <date class="post-meta meta-date">
                            2018年11月19日
                        </date>
                        
                        <div class="post-meta">
                            <span>|</span>
                            
                                <span class="meta-category"><a href="https://otswang.gitee.io/hugo/categories/smallquickappweb">SmallQuickAppWeb</a></span>
                            
                        </div>
                        
                        
                        
                        <div class="post-content">
                            <p>阮一峰ES6标准入门学习</p>

<p><center> 概览 </center></p>

<h2 id="历史">历史</h2>

<p>ES6是JavaScript的一个标准，旨在将JavaScript运用在大型应用项目中。ES6是相对ES5而言，并于2015年6月发布第一个版本，以后每年的6月份发布新版本，并以年份命名，比如ES6一般指ES2015，其后发布了ES2016，ES2017等，但都属于ES6标准。</p>

<p>可以使用babel工具将ES6的代码转为ES5的代码。<em>此部分先不看，后期再说</em></p>

<p><center> let和const命令 </center></p>

<h2 id="let命令">let命令</h2>

<p>let的作用域为声明的代码块，var则不然，如下：</p>

<pre><code class="language-javascript">{
    let a = 10;
    var b = 12;
}
console.log(a);     // a is not defined
console.log(b);     // 1
</code></pre>

<p>思考如下代码：</p>

<pre><code class="language-javascript">var a = [];
for (let i=0; i&lt;10; i++) {
  a[i] = function (){
    console.log(i);
  };
}

a[6]();
// 当在for中使用let时，输出 6
// 当在for中使用var时，输出 10
</code></pre>

<p>分析：<br/>
上面代码中，变量i是var声明时，在全局范围都有效，所以<strong>全局只有一个变量i</strong>。每一次循环，变量i的值都会发生改变，而循环内部被赋予给数组a的函数内部的console.log(i)，里面的<strong>i指向的就是全局的i</strong>，也就是说，所有数组a的成员里面的i，执行的都是同一个i，导致运行时输出的是最后一轮i的值，当用let声明时，变量只在块级作用域有效。</p>

<p>思考如下代码：</p>

<pre><code class="language-javascript">for (let i = 0; i &lt; 3; i++) {
  let i = 'abc';
  console.log(i);
}
</code></pre>

<p>分析：<br/>
在for的条件判断中使用let可以正常执行，但是使用var在报错。说明函数内部的i和循环变量i不在同一个作用域，有各自独立的作用域。</p>

<p>let 不存在变量提升。（在var中是可以进行先使用变量再声明的，但是获取变量时会取得 undefined）</p>

<p>暂时性死区(temporal dead zone, TDZ)： ES6规定，如果区块中存在let或const命令，这个区块对这些命令声明的变量就会从一开始就形成封闭作用域，在声明之前使用变量就会报错。如下代码示例：</p>

<pre><code class="language-javascript">var tmp = 123;
if (true){
    tmp = 234; // referenceError
    let tmp;
}
</code></pre>

<p>分析如下代码思考tdz造成的typeof情况：</p>

<pre><code class="language-javascript">typeof a;
/*
undefined
*/

{
  typeof b;
  let b=5;
}
/*
Exception: ReferenceError: can't access lexical declaration `b' before initialization
@Scratchpad/1:2:3
*/
</code></pre>

<h2 id="块级作用域">块级作用域</h2>

<p><em>不是很理解</em></p>

<p>思考如下代码：</p>

<pre><code class="language-js"> function f() { console.log('I am outside!'); }

(function () {
  if (false) {
    // 重复声明一次函数f
    function f() { console.log('I am inside!'); }
  }

  f();
}());
// Exception: TypeError: f is not a function
</code></pre>

<h2 id="const命令">const命令</h2>

<p>const本质上保证的是变量指向的内存地址所保存的数据不得改动。对于常量，地址对应的就是变量内容，而对于对象，地址保存的是对象的引用，只能保证此时执行的对象不发生变化，但是对象的内容可以发生变化。如下代码：</p>

<pre><code class="language-js">const foo = {};
const b = 3;

foo.prop = 123;

foo.prop
/*
3
*/

b = 4;
/*
Exception: SyntaxError: redeclaration of const foo
@Scratchpad/1:1:1
*/

foo = {}
/*
Exception: SyntaxError: redeclaration of const foo
@Scratchpad/1:1:1
*/
</code></pre>

<h2 id="es6声明变量的六种方式">ES6声明变量的六种方式</h2>

<ul>
<li>var</li>
<li>function</li>
<li>let</li>
<li>const</li>
<li>import</li>
<li>class</li>
</ul>

<h2 id="顶层对象">顶层对象</h2>

<p>浏览器环境中指的是Window对象，在node中指的是global对象。</p>

<p>思考如下代码：</p>

<pre><code class="language-js">var a = 1;
window.a
/*
1
*/
let b = 2;
window.b
/*
undefined
*/
</code></pre>

<p>分析:</p>

<p>顶层对象的属性与全局变量挂钩，被认为是JavaScript语言最大的设计败笔之一。ES6为了保持兼容性，规定var命令和function命令声明的全局变量依然是顶层对象的属性；同时也规定，let命令，const命令和class命令声明的全部变量，不属于顶层对象的属性。即<strong>从ES6开始，全局变量将逐步与顶层对象的属性脱钩</strong>。</p>

<h2 id="global-对象">global 对象</h2>

<p>由于顶层对象的设计实现不统一 ，在浏览器和node中不一致，导致顶层对象的使用很是麻烦，再此不多学习，后续用到再学习。</p>

<p><center> 变量的解构赋值 </center></p>

<h2 id="数组的解构赋值">数组的解构赋值</h2>

<p>ES6 允许按照一定模式，从数组和对象中取值，对变量进行赋值，这被称为解构。现在还多语言都设计了此种模式。如下代码;</p>

<pre><code class="language-js">let [a,b,c] = [1,2,3]

console.log(a)
console.log(b)
console.log(c)
</code></pre>

<h3 id="默认值">默认值</h3>

<p>解构赋值允许指定默认值。如下代码：</p>

<pre><code class="language-js">let [foo = true] = [];
let [x,y=100] = [1,2];
let [x=1] = [null];
// x = null
</code></pre>

<p>只有在数组中严格等于undefined时才使用默认值</p>

<h3 id="对象的解构赋值">对象的解构赋值</h3>

<pre><code class="language-js">let {foo, bar} = {foo: &quot;test&quot;, bar: &quot;word&quot;};
let {say, what} = {what: &quot;word&quot;, say: &quot;hello&quot;};
// hello word
</code></pre>

<p><em>似乎有个叫做模式的东西（括号）导致很难理解，等到用时再说</em></p>

<h3 id="用途">用途</h3>

<ol>
<li><p>交换变量的值</p>

<pre><code class="language-js">let x = 1;
let y = 2;
[x,y] = [y,x];
</code></pre></li>

<li><p>从函数返回多个值</p>

<pre><code class="language-js">function test() {
    return [1,2,3,4];
}

let [a,b,c,d] = test();
</code></pre></li>

<li><p>函数参数的定义</p></li>
</ol>

<p>可以将一组参数与变量名对应起来。</p>

<ol>
<li><p>提取json数据</p>

<pre><code class="language-js">let jsonData = {
  id : 43,
  status : &quot;ok&quot;,
  data: [
    867,
    530
  ]
};

let {id, status, data: number} = jsonData;

console.log(id, status, number);
</code></pre></li>

<li><p>遍历Map结构</p>

<pre><code class="language-js">const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');

for (let [key, value] of map) {
    console.log(key + &quot; is &quot; + value);
}
</code></pre></li>
</ol>

<p><center> 字符串的拓展 </center></p>

<h2 id="unicode">unicode</h2>

<p>使用{}可以表示一个大于oxffff的字符，在ES6之后，JavaScript共有6种方法表示一个字符：</p>

<pre><code class="language-js">'\z' === 'z';
'\172' === 'z';
'\x7a' === 'z';
'\u007a' === 'z';
'\u{7a}' === 'z';
</code></pre>

<p>&hellip;</p>

<h2 id="字符串常用函数">字符串常用函数</h2>

<ul>
<li>includes()</li>
<li>startsWidth()</li>
<li>endsWith()</li>
<li>repeat()</li>
<li>padStart() padEnd() 用于补全</li>

<li><p>matchAll() 用于匹配字符串</p>

<pre><code class="language-js">let s = &quot;hello, world&quot;;

s.startsWith('hello');
/*
true
*/
s.endsWith('ld');
/*
true
*/
s.includes('o');
/*
true
*/
s.includes('O');
/*
false
*/

s.repeat(3);
/*
hello, worldhello, worldhello, world
*/
</code></pre></li>
</ul>

<h2 id="模版字符串">模版字符串</h2>

<h3 id="初识">初识</h3>

<p>模版字符串(template string) 是增强版的字符串，使用反引号表示,可以当作普通字符串使用，也可以用来定义多行字符串，或者在字符串中嵌入变量。<strong>大括号中可以放入任意的JavaScript表达式，可以进行运算，以及引用对象属性等</strong></p>

<p>思考如下代码：</p>

<pre><code class="language-js">`in javascript '\n' is a line-feed`;

/*
in javascript '
' is a line-feed
*/

`in javascript this is not 
legal.`
/*
in javascript this is not 
legal.
*/

let name = &quot;bob&quot;, time = &quot;today&quot;;
`hello, ${name}, how are you ${time}?`
/*
hello, bob, how are you today?
*/
</code></pre>

<p><center> 正则表达式 </center></p>

<h2 id="regexp构造函数">RegExp构造函数</h2>

<h3 id="es6的正则表达式">ES6的正则表达式</h3>

<p>与ES5不同，允许第二个参数为字符串。如下：</p>

<pre><code class="language-js">var regex = new RegExp(/xyz/, 'i');
// 在es5下是会报错的，在es6下不会
</code></pre>

<h3 id="字符串的正则表达式">字符串的正则表达式</h3>

<p>字符串共有四种方法进行正则表达式：<code>match(), replace(), search(), split()</code>。
es6的这四种方法，在语言内部全部调用RegExp的实例方法</p>

<h3 id="u修饰符">u修饰符</h3>

<p>。。。</p>

<h2 id="javascript正则语法">JavaScript正则语法</h2>

<p>From 菜鸟学院</p>

<h4 id="基本语法">基本语法</h4>

<pre><code class="language-js">/正则表达式主体/修饰符(可选)
</code></pre>

<h4 id="正则表达式模式">正则表达式模式</h4>

<ul>
<li>方括号用于查找范围</li>
<li>元字符拥有特殊含义 <code>\d, \s, \b, \uxxxx</code></li>
<li>量词 <code>n+</code>至少n个，<code>n*</code>包含0或n个，<code>n?</code>表示0或1个
<br /></li>
</ul>

<h4 id="修饰符">修饰符</h4>

<table>
<thead>
<tr>
<th>表达式</th>
<th>描述</th>
</tr>
</thead>

<tbody>
<tr>
<td>i</td>
<td>大小写不敏感</td>
</tr>

<tr>
<td>g</td>
<td>执行全局匹配 ，而不是在匹配到第一个后就停止</td>
</tr>

<tr>
<td>m</td>
<td>执行多行匹配</td>
</tr>
</tbody>
</table>

<h4 id="方括号">方括号</h4>

<table>
<thead>
<tr>
<th>表达式</th>
<th>描述</th>
</tr>
</thead>

<tbody>
<tr>
<td>[abc]</td>
<td>abc</td>
</tr>

<tr>
<td>[^abc]</td>
<td>非abc</td>
</tr>

<tr>
<td>[0-9]</td>
<td>0-9</td>
</tr>

<tr>
<td>[a-z]</td>
<td>a-z</td>
</tr>

<tr>
<td><code>(red\|ad\|dc)</code></td>
<td>三个中一个</td>
</tr>
</tbody>
</table>

<h4 id="元字符">元字符</h4>

<table>
<thead>
<tr>
<th>元字符</th>
<th>描述</th>
</tr>
</thead>

<tbody>
<tr>
<td><code>.</code></td>
<td>单个字符，除了换行符和行结束符</td>
</tr>

<tr>
<td><code>\w</code></td>
<td>查找单词</td>
</tr>

<tr>
<td><code>\W</code></td>
<td>查找非单词</td>
</tr>

<tr>
<td><code>\d</code></td>
<td>数字</td>
</tr>

<tr>
<td><code>\D</code></td>
<td>非数字</td>
</tr>

<tr>
<td><code>\s</code></td>
<td>空白字符</td>
</tr>

<tr>
<td><code>\S</code></td>
<td>非空白字符</td>
</tr>

<tr>
<td><code>\b</code></td>
<td>匹配单词边界</td>
</tr>

<tr>
<td><code>\B</code></td>
<td>匹配非单词边界</td>
</tr>

<tr>
<td><code>\0</code></td>
<td>查找null字符串</td>
</tr>

<tr>
<td><code>\n</code></td>
<td>换行符</td>
</tr>

<tr>
<td><code>\f</code></td>
<td>换页符</td>
</tr>

<tr>
<td><code>\r</code></td>
<td>回车符</td>
</tr>

<tr>
<td><code>\t</code></td>
<td>制表符</td>
</tr>

<tr>
<td><code>\v</code></td>
<td>垂直制表符</td>
</tr>
</tbody>
</table>

<h4 id="量词">量词</h4>

<table>
<thead>
<tr>
<th>量词</th>
<th>描述</th>
</tr>
</thead>

<tbody>
<tr>
<td><code>n+</code></td>
<td>至少一个</td>
</tr>

<tr>
<td><code>n*</code></td>
<td>任意多个</td>
</tr>

<tr>
<td><code>n?</code></td>
<td>一个或零个</td>
</tr>

<tr>
<td><code>n{X}</code></td>
<td>X个</td>
</tr>

<tr>
<td><code>n{X,}</code></td>
<td>至少X个</td>
</tr>

<tr>
<td><code>n{X,Y}</code></td>
<td>X-Y个</td>
</tr>

<tr>
<td><code>n$</code></td>
<td>以n结尾</td>
</tr>

<tr>
<td><code>^n</code></td>
<td>以n开始</td>
</tr>

<tr>
<td><code>?=n</code></td>
<td>匹配任何结尾为n的字符串</td>
</tr>

<tr>
<td><code>?!n</code></td>
<td>匹配任何结尾不为n的字符串</td>
</tr>
</tbody>
</table>

<h4 id="贪婪匹配与惰性匹配">贪婪匹配与惰性匹配</h4>

<p>用贪婪量词进行匹配时，首先将整个字符串当做一个匹配，如果匹配则退出，否则继续去掉最后一个。惰性量词进行匹配时，首先将第一个字符当作一个匹配，如果成功则退出，否则继续加字符。惰性量词只是在量词后加？，如<code>a+</code>是贪婪匹配，则<code>a+?</code>则为惰性的。</p>

<pre><code class="language-js">str = &quot;abc&quot;;
/\w+/.exec(str)[0]
/*
abc
*/

re = /\w+?/;
re.exec(str)[0]
/*
a
*/
</code></pre>

<h4 id="分组与非捕获性分组">分组与非捕获性分组</h4>

<p>非捕获性分组使用(?:)</p>

<pre><code class="language-js">var re = /abc{2}/;
var str = &quot;abcabcc ###&quot;;
re.exec(str)[0]
/* 无分组
abcc
*/
re = /(abc){2}/;
re.exec(str)[1]
/* 捕获性分组
abc
*/

re = /(?:abc){2}/;
re.exec(str)[1]
/* 非捕获性分组
undefined
*/
</code></pre>

<h4 id="正向前瞻和负向前瞻">正向前瞻和负向前瞻</h4>

<p>正向前瞻：用来捕获出现在特定字符之前的字符，只有当字符后面跟着某个特定字符才去捕获它；负向前瞻：用来匹配只有当字符后面不跟着某个特定字符时才去匹配它。</p>

<pre><code class="language-js">// 正向前瞻(?=)
var re = /([a-z]+(?=\d))/i;
// 我们要匹配后面跟一个数字的单词，将单词返回，而不要返回数字
var str = &quot;abc test1 abcd&quot;;
re.exec(str)[0]
/*
test
*/

// 负向前瞻(?!)
re = /([a-z])+(?!\d)/i;
re.exec(str)[0]
/*
abc
*/
</code></pre>

<h4 id="regexp-对象">regexp 对象</h4>

<table>
<thead>
<tr>
<th>方法</th>
<th>描述</th>
</tr>
</thead>

<tbody>
<tr>
<td><code>exec</code></td>
<td>检索字符串中制定的值，并确定位置</td>
</tr>

<tr>
<td><code>test</code></td>
<td>检索字符串中制定的值，返回ture或false</td>
</tr>

<tr>
<td><code>toString</code></td>
<td>string</td>
</tr>
</tbody>
</table>

<table>
<thead>
<tr>
<th>属性</th>
<th>描述</th>
</tr>
</thead>

<tbody>
<tr>
<td>global</td>
<td>g</td>
</tr>

<tr>
<td>ignoreCase</td>
<td>i</td>
</tr>

<tr>
<td>lastIndex</td>
<td>下次匹配起始位置</td>
</tr>

<tr>
<td>multiline</td>
<td>m</td>
</tr>

<tr>
<td>source</td>
<td>返回正则表达式的匹配模式</td>
</tr>
</tbody>
</table>

<h4 id="一个例子">一个例子</h4>

<p>构建一个验证邮箱地址的正则表达式：用户名只包含字母数字和下划线，最少一位，最多25位，用户名后面紧跟@，后面是域名，域名要求只能包含字母和减号，并且不能以减号开始或结尾，然后是域名后缀，域名后缀必须是点号连上2-4位英文字母。</p>

<pre><code class="language-js">var re = /^\w{1,25}(?:@(?!-))(?:(?:[a-z0-9-]*)(?:[a-z0-9](?!-))(?:\.(?!-)))+[a-z]{2,4}$/;
</code></pre>

<p>参考：
(精通 JavaScript中的正则表达式)[<a href="https://www.cnblogs.com/mq0036/p/4836439.html">https://www.cnblogs.com/mq0036/p/4836439.html</a>]</p>

<p><center> 数组的扩展 </center></p>

<h2 id="number对象">Number对象</h2>

<pre><code class="language-js">Number.parseInt('12.34');
/*
12
*/
Number.parseFloat('123.345@');
/*
123.345
*/
Number.isInteger(25.0);
/*
true
*/
Number.MIN_VALUE;
/*
5e-324
*/
Number.EPSILON;
/*
2.220446049250313e-16
*/
</code></pre>

<h2 id="math-对象">Math 对象</h2>

<pre><code class="language-js">Math.trunc(4.1);
/* 去除小数部分
4
*/
Math.trunc(-4.1);
/*
-4
*/
Math.trunc(-4.7);
/*
-4
*/
Math.sign(-5);
/*
-1
*/
Math.sign(0);
/*
0
*/
Math.cbrt(8);
/*
2
*/
Math.clz32(1)
/* count leading zero bits in 32-bit binary representation of a number
31
*/
Math.hypot(3,4);
/* 计算所有参数平方和的平方根
5
*/
Math.expm1(1);
/* e^x - 1
1.718281828459045
*/
Math.log1p(2.71828-1);
/* log(x+1)
0.999999327347282
*/
Math.log10(10);
/*
1
*/
Math.log2(2);
/*
1
*/
2 ** 3
/* 指数运算 (与Math.pow不同，当数很大时会有区别)
8
*/
</code></pre>

<p><center> 对象的拓展 </center></p>

<p>重新学习javascript 的对象。</p>

<h2 id="symbol">Symbol</h2>

<p>es5的对象属性名都是字符串，容易造成属性名的冲突，es6引入新的数据类型symbol，表示独一无二的值。它是javascript的第七种数据类型，前六种是：<code>undefined, null, Boolean, String, Number, Object</code>。Symbol的值通过<code>Symbol()</code>函数生成。</p>

<p>每一个Symbol对象都是不相等的，这意味着Symbol值可以作为标识符，用于对象的属性名，就能保证不会出现同名的属性，这对于一个对象由多个模块构成的情况非常有用，防止键被改写或覆盖。</p>

<h2 id="set-和-map-数据结构">Set 和 Map 数据结构</h2>

<pre><code class="language-js">var s  = new Set();

[2,3,9,5,6,4,3,8].forEach(x =&gt; s.add(x));

[...s]
/*
2,3,9,5,6,4,8
*/
</code></pre>

<p>Set的操作方法：</p>

<ul>
<li><code>add(value)</code></li>
<li><code>delete(value)</code></li>
<li><code>has(value)</code></li>
<li><code>clear()</code>
<br /></li>
</ul>

<p>遍历方法：</p>

<ul>
<li><code>keys()</code></li>
<li><code>values()</code></li>
<li><code>entries()</code></li>
<li><code>forEach()</code></li>
</ul>

<p>使用set实现的交并补;</p>

<pre><code class="language-js">let a = new Set([1,2,3]);
let b = new Set([4,3,2]);

// 并集
let union = new Set([...a, ...b]);
[...union]
/*
1,2,3,4
*/

// 交集
let intersect = new Set([...a].filter(x =&gt; b.has(x)));
[...intersect]
/*
2,3
*/

// 差集
let difference = new Set([...a].filter(x =&gt; !b.has(x)));
[...difference]
/*
1
*/
</code></pre>

<p>WeakSet:</p>

<p>WeakSet 结构与Set 类似，其区别为：</p>

<ul>
<li>WeakSet 内成员只能是对象，不能是普通类型</li>
<li>WeakSet 为弱引用，垃圾回收机制不会考虑该引用</li>
</ul>

<p>Map ，同Set 。</p>

<h2 id="proxy">Proxy</h2>

<h2 id="reflect">Reflect</h2>

<h2 id="promise">Promise</h2>

<p>异步编程的一种解决方案，比传统的解决方案（回调函数和事件）更加合理和强大。所谓Promise，简单来说就是一个容器，保存着未来才会结束的事件的结果。从语法上说，Promise 是一个对象，从它可以获取异步操作的消息。</p>

<p>Promise 有两个特点：</p>

<ul>
<li>对象状态不受外界影响。
共三种状态，<code>peding, fulfilled, rejected</code> 。只有异步操作的结果才可以决定当前是哪一种状态，任何操作都无法改变这个状态(即承诺)</li>
<li>一旦改变状态，将不会改变，任何时候都可以得到结果。
如果改变已经发生，再对Promise对象添加回调函数，也会立即得到结果，这与event完全不同，事件的特点是错过列再去监听将无法得到结果。</li>
</ul>

<p>有了Promise对象，就可以将异步操作以同步操作的流程表达出来，避免列层层嵌套的回调函数。</p>

<p><em>如果事件不断地反复发生，使用Stream模式比部署Promise更好的选择。</em></p>

<pre><code class="language-js">let promise = new Promise(function(resolve, reject){
  console.log('Promise');
  resolve();
});

promise.then(function(){
    console.log('resolved');
});

console.log('hi');

/*
Promise 
hi 
resolved
*/
</code></pre>

<p>上面的代码中，Promise新建后立即执行，所以先输出Promise，然后then指定的回调函数，将在当前脚本所有同步任务执行完后再执行，所以resolved最后输出。</p>

<p>一个使用Promise对象实现ajax操作的例子：</p>

<pre><code class="language-js">const getJSON = function(url){
  const promise = new Promise(function(resolve, reject){
    const handler = function(){
      if(this.readyState !== 4){
        return ;
      }
      if (this.status === 200 ){
        resolve(this.responce);
      } else {
        reject(new Error(this.statusText));
      }
    };
    
    const client = new XMLHttpRequest();
    client.open(&quot;GET&quot;, url);
    client.onreadystatechange = handler;
    client.responseType = &quot;json&quot;;
    client.setRequestHeader(&quot;Accept&quot;, &quot;application/json&quot;);
    client.send();
    
  });
  return promise;
};

getJSON(&quot;/posts.json&quot;).then(function(json){
  console.log(&quot;contents: &quot;+json);
}, function(error){
  console.error(&quot;出错了&quot;, error);
});
</code></pre>

<p>应用：</p>

<pre><code class="language-js">const preloadImage = function(path){
  return new Promise(function(resolve, reject){
    const image = new Image();
    iamge.onload = resolve;
    image.onerror = reject;
    image.src = path;
  });
};

preloadeImage(&quot;/test.jpg&quot;).then(function(){
  console.log('图片加载完成');
})
</code></pre>

<h2 id="廖雪峰的官方网站">廖雪峰的官方网站</h2>

<p><a href="https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/0014345008539155e93fc16046d4bb7854943814c4f9dc2000">https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/0014345008539155e93fc16046d4bb7854943814c4f9dc2000</a></p>

<p>Promise：
先执行逻辑代码，不关心处理结果，最后，根据结果是成功还是失败选择调用success还是fail函数。这种承诺将来会执行的对象被成为Promise对象。</p>

<h2 id="es6中的三种异步编码方式">es6中的三种异步编码方式</h2>

<ul>
<li>Generator函数返回一个迭代器，在迭代器上可以调用<code>next()</code>方法，执行下一个yield或return</li>
<li>调用<code>next()</code>方法，返回一个对象<code>{value:res, done:false}</code>，value的值为yield之后表达式值，done为一个迭代器，标识是否已经执行完毕（最后一个yield或return）</li>
<li><code>next()</code>方法，可以传入一个值，作为前一个的yield表达式的返回值。</li>
</ul>

<h3 id="方式一-generator-promise-执行器">方式一 Generator + Promise + 执行器</h3>

<pre><code class="language-js">const fs = require('fs');

// Promise 版的readFile
const readFile = function (fileName) {
    return new Promise(function(resolve, reject){
        fs.readFile(fileName, function(err, data){
            if (err){
                return reject(err);
            }
            resolve(data);
        });
    });
};

const gen = function * () {
    let f1 = yield readFile('a.txt');
    let f2 = yield readFile('b.txt');

    console.log(`F1----&gt;`, f1.toString());
    console.log(`F2----&gt;`, f2.toString());

};

// 基于Generator 和 Promise 的自动执行器
function run(gen){
    let g = gen();
    function next(data){
        let result = g.next(data);
        if (result.done){
            return result.value;
        }

        result.value.then(function (data) {
            next(data);
        });
    }

    next();
}

run(gen);
</code></pre>

<h3 id="方式二-generator-thunk函数-执行器">方式二 Generator + Thunk函数 + 执行器</h3>

<pre><code class="language-js">const fs = require('fs')

// 把一个单一执行的函数 ，变成需要再次调用的函数，固定一部分参数
function thunkify(fn, obj = {}) {
    return function () {
        let args = Array.from(arguments);
        return function (m) {
            args.push(m)
            return fn.apply(obj, args)
        }
    }
}

const readFile = thunkify(fs.readFile, fs);

const gen = function* () {
    let f1 = yield readFile('a.txt');
    let f2 = yield readFile('b.txt');

    console.log('F1--&gt;', f1.toString());
    console.log('F2--&gt;', f2.toString());
}


// 基于 Generator 和 Thunk函数的自动执行器
function run(fn) {
    let gen = fn();

    function next(err, data) {
        let result = gen.next(data);
        if (result.done) return 1;
        result.value(next);
    }

    next();

}

run(gen);
</code></pre>

<h3 id="方式三-基于async函数和await的异步处理方式">方式三 基于async函数和await的异步处理方式</h3>

<pre><code class="language-js">const fs = require('fs')

// Promise 版的readFile
const readFile = function (fileName) {
    return new Promise(function (resolve, reject) {
        fs.readFile(fileName, function (err, data) {
            if (err) return reject(err);
            resolve(data);
        })
    })
}

const asyncReadFile = async function () {
    const f1 = await readFile('a.txt');
    const f2 = await readFile('b.txt');
    console.log(f1.toString());
    console.log(f2.toString());
};

asyncReadFile();
</code></pre>

<h2 id="class与javascript类概念">Class与JavaScript类概念</h2>

<p>在JavaScript中，生成实例对象的传统方法是通过构造函数，如下：(与传统面向对象语音差别很大，容易困惑)</p>

<pre><code class="language-js">function Point(x,y){
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function(){
  return '(' + this.x + ',' + this.y + ')';
}

var p = new Point(2,3);
/*
function(){
  return '(' + this.x + ',' + this.y + ')';
}
*/
</code></pre>

<p>es6引入class，如下代码：</p>

<pre><code class="language-js">class Point {
  // constructor 默认构造函数
  constructor(x,y){
    this.x = x;
    this.y = y;
  }
  
  toString() {
    return '(' + this.x + ',' + this.y + ')';
  }
}

typeof Point
/*
function
*/

var p = new Point(3,4);
p.toString();
/*
(3,4)
*/
p.hasOwnProperty('x');
/*
true
*/
p.hasOwnProperty('toString');
/*
false
*/
</code></pre>

<p><strong>严格模式</strong></p>

<p>类和模块内部默认就是严格模式，因此不再需要使用<code>use strict</code>指定运行模式。</p>

<p>与es5一样，实例的属性除非显式定义在其本身上(即定义在<code>this</code>对象上)，否则都属于定义在原型上。</p>

<p>es6目前不支持私有属性，（有多种模拟方法，暂不考虑）</p>

<h2 id="继承">继承</h2>

<pre><code class="language-js">
class Point {
  constructor(x,y){
    this.x = x;
    this.y = y;
    console.log('point');
  }

  toString() {
    return '(' + this.x + ',' + this.y + ')';
  }
}

class ColorPoint extends Point {
  constructor(x,y,color){
    super(x,y);
    this.color = color;
    console.log('color point');
  }
  
  toString() {
    return this.color + ' ' + super.toString();
  }
}

var p = new Point(3,4);
var pc = new ColorPoint(3,4,5);

p.toString();
/*
(3,4)
*/

pc.toString();
/*
5 (3,4)
*/
/** console.out
 * point
 * color point
 */
</code></pre>

<h3 id="类的prototype属性和-proto-属性">类的prototype属性和<strong>proto</strong>属性</h3>

<p>大多数浏览器的es5实现中，每一个对象都有<strong>proto</strong>属性，指向对应的构造函数的prototype属性。class作为构造函数的语法糖，同时有prototype属性和<strong>proto</strong>属性，因此同时存在两条继承链。</p>

<p>观察下面的代码：</p>

<pre><code class="language-js">class A {
}

class B extends A {
}

B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
</code></pre>

<p>分析：</p>

<p>上面代码中，子类B的<strong>proto</strong>属性指向父类A，子类B的prototype属性的<strong>proto</strong>属性指向父类A的prototype属性。</p>

<h2 id="module-的语法与使用">Module 的语法与使用</h2>

<p>严格模式</p>

<ul>
<li>变量必须声明后再使用</li>
<li>函数的参数不能有同名属性</li>
<li>不能使用with语句</li>
<li>不能对只读属性赋值</li>
<li>不能使用前缀0表示八进制数</li>
<li>禁止使用this指向全局对象
&hellip;</li>
</ul>

<h3 id="export命令与import命令">export命令与import命令</h3>

<pre><code class="language-js">// a.js
var a = 1;
var b = 2;
function sayHello(){
  console.log('aaa');
}

export {
  a as a,
  b as b,
  sayHello as output
}


// b.js
import {a, b, output} from 'a.js';
console.log(a);
output();
// 也可以
import * as A from 'a.js';
console.log(A.a);
</code></pre>

<h2 id="编程风格">编程风格</h2>

<p>多家公司和组织已经公开了它们的风格规范，下面的内容主要参考Airbnb公司的JavaScript规范。</p>

<p><strong>块级作用域</strong></p>

<ul>
<li>let 取代 var</li>
<li>全局常量和线程安全（let和const之间优先使用const）</li>
</ul>

<p><strong>字符串</strong></p>

<p>优先使用单引号或反引号，动态字符串使用反引号。</p>

<pre><code class="language-js">const a = 'foobar';       // 单引号
const b = `foo${a}bar`;   // 反引号
</code></pre>

<p><strong>解构赋值</strong></p>

<p>使用数组成员对变量赋值时，优先使用解构赋值</p>

<pre><code class="language-js">const arr = [1,2,3,4];
// bad 
const a = arr[0];
const b = arr[1];
// good
const [c,d] = arr;
</code></pre>

<p>如果函数返回多个值，优先使用对象的解构赋值，而不是数组的解构赋值。</p>

<p><strong>对象</strong></p>

<p>单行定义的对象，最后一个成员不以逗号结尾，多行定义的对象，最后一个成员以逗号结尾。</p>

<pre><code class="language-js">const a = {k1:v1, k2:v2, k3:v3};
const b = {
  k1: v1,
  k2: v2,
  k3: v3,
}
</code></pre>

<p><strong>数组</strong></p>

<p>使用(&hellip;) 拷贝数组</p>

<p><strong>函数</strong></p>

<p>多使用lambda表达式</p>

<p><strong>Map 结构</strong></p>

<p>注意区分 Object 和 Map，只有模拟现实世界的实体对象时，才使用 Object。如果只是需要key: value的数据结构，使用 Map 结构。因为 Map 有内建的遍历机制。</p>

<p><strong>class</strong></p>

<p>总是用 Class，取代需要 prototype 的操作。因为 Class 的写法更简洁，更易于理解。（使用class而不是function）</p>

<p><strong>模块</strong></p>

<ul>
<li><p>使用import取代require</p>

<pre><code class="language-js">// bad 
const moduleA = require('moduleA');
const func1 = moduleA.func1;
// good
import {func1, func2 } from 'moduleA';
</code></pre></li>

<li><p>使用export取代module.exports</p>

<pre><code class="language-js">// bad 
var aa = 100;
module.exports = {
  aa : aa,
}

// good
export default aa;
</code></pre></li>
</ul>

<p><strong>eslint 的使用</strong></p>

<p>代码检查</p>
                        </div>

                        


                        


                        <div class="post-meta meta-tags">
                            
                            <ul class="clearfix">
                                
                                <li><a href="https://otswang.gitee.io/hugo/tags/javascript">JavaScript</a></li>
                                
                            </ul>
                            
                        </div>
                    </article>
                    
    

    
    
                </div>
            </div>
            <div id="secondary">

    <section class="widget">
        <form id="search" action="//www.google.com/search" method="get" accept-charset="utf-8" target="_blank" _lpchecked="1">
      
      <input type="text" name="q" maxlength="20" placeholder="Search">
      <input type="hidden" name="sitesearch" value="https://otswang.gitee.io/hugo/">
      <button type="submit" class="submit icon-search"></button>
</form>
    </section>

    
    <div class="clear">
        <div class="toc-article">
            <div class="toc-title">文章目录</dixsv>
            <nav id="TableOfContents">
<ul>
<li>
<ul>
<li><a href="#历史">历史</a></li>
<li><a href="#let命令">let命令</a></li>
<li><a href="#块级作用域">块级作用域</a></li>
<li><a href="#const命令">const命令</a></li>
<li><a href="#es6声明变量的六种方式">ES6声明变量的六种方式</a></li>
<li><a href="#顶层对象">顶层对象</a></li>
<li><a href="#global-对象">global 对象</a></li>
<li><a href="#数组的解构赋值">数组的解构赋值</a>
<ul>
<li><a href="#默认值">默认值</a></li>
<li><a href="#对象的解构赋值">对象的解构赋值</a></li>
<li><a href="#用途">用途</a></li>
</ul></li>
<li><a href="#unicode">unicode</a></li>
<li><a href="#字符串常用函数">字符串常用函数</a></li>
<li><a href="#模版字符串">模版字符串</a>
<ul>
<li><a href="#初识">初识</a></li>
</ul></li>
<li><a href="#regexp构造函数">RegExp构造函数</a>
<ul>
<li><a href="#es6的正则表达式">ES6的正则表达式</a></li>
<li><a href="#字符串的正则表达式">字符串的正则表达式</a></li>
<li><a href="#u修饰符">u修饰符</a></li>
</ul></li>
<li><a href="#javascript正则语法">JavaScript正则语法</a>
<ul>
<li>
<ul>
<li><a href="#基本语法">基本语法</a></li>
<li><a href="#正则表达式模式">正则表达式模式</a></li>
<li><a href="#修饰符">修饰符</a></li>
<li><a href="#方括号">方括号</a></li>
<li><a href="#元字符">元字符</a></li>
<li><a href="#量词">量词</a></li>
<li><a href="#贪婪匹配与惰性匹配">贪婪匹配与惰性匹配</a></li>
<li><a href="#分组与非捕获性分组">分组与非捕获性分组</a></li>
<li><a href="#正向前瞻和负向前瞻">正向前瞻和负向前瞻</a></li>
<li><a href="#regexp-对象">regexp 对象</a></li>
<li><a href="#一个例子">一个例子</a></li>
</ul></li>
</ul></li>
<li><a href="#number对象">Number对象</a></li>
<li><a href="#math-对象">Math 对象</a></li>
<li><a href="#symbol">Symbol</a></li>
<li><a href="#set-和-map-数据结构">Set 和 Map 数据结构</a></li>
<li><a href="#proxy">Proxy</a></li>
<li><a href="#reflect">Reflect</a></li>
<li><a href="#promise">Promise</a></li>
<li><a href="#廖雪峰的官方网站">廖雪峰的官方网站</a></li>
<li><a href="#es6中的三种异步编码方式">es6中的三种异步编码方式</a>
<ul>
<li><a href="#方式一-generator-promise-执行器">方式一 Generator + Promise + 执行器</a></li>
<li><a href="#方式二-generator-thunk函数-执行器">方式二 Generator + Thunk函数 + 执行器</a></li>
<li><a href="#方式三-基于async函数和await的异步处理方式">方式三 基于async函数和await的异步处理方式</a></li>
</ul></li>
<li><a href="#class与javascript类概念">Class与JavaScript类概念</a></li>
<li><a href="#继承">继承</a>
<ul>
<li><a href="#类的prototype属性和-proto-属性">类的prototype属性和<strong>proto</strong>属性</a></li>
</ul></li>
<li><a href="#module-的语法与使用">Module 的语法与使用</a>
<ul>
<li><a href="#export命令与import命令">export命令与import命令</a></li>
</ul></li>
<li><a href="#编程风格">编程风格</a></li>
</ul></li>
</ul>
</nav>
        </div>
    </div>
    

</div>
        </div>
    </div>
</div>
<footer id="footer">
    <div class="container">
        &copy; 2020 <a href="https://otswang.gitee.io/hugo/">The Sky of OtsWang By OtsWang</a>.
        Powered by <a rel="nofollow noreferer noopener" href="https://gohugo.io" target="_blank">Hugo</a>.
        <a href="https://www.flysnow.org/" target="_blank">Theme</a> based on <a href="https://github.com/Dudiao137/maupassant-hugo" target="_blank">maupassant-ots</a>.
        
    </div>
</footer>


    <script type="text/javascript">
    
    (function(){
        $("pre code").parent().addClass("line-numbers")
    }())

    window.MathJax = {
        tex2jax: {
            inlineMath: [ ['$','$'] ],
            processEscapes: true
        }
    };
    </script>
    <script type="text/javascript" src="/hugo/js/prism.js" async="true"></script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML' async></script>

<a id="rocket" href="#top"></a>
<script type="text/javascript" src="/hugo/js/totop.js?v=0.0.0" async=""></script>







 
 <script src="https://mermaidjs.github.io/scripts/mermaid.min.js"></script>
 <script>
       mermaid.initialize({ startOnLoad: true });
 </script>
</body>
</html>
